package secretmanager

import (
	"context"
	"errors"
	"net/http"
	"net/url"
	"path"
	"strconv"

	"github.com/solsw/errorhelper"
	"github.com/solsw/generichelper"
	"github.com/solsw/httphelper"
)

type (
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#
	Version struct {
		CreatedAt string `json:"createdAt,omitempty"`
		Id        int    `json:"id,omitempty"`
		SecretId  string `json:"secretId,omitempty"`
		State     string `json:"state,omitempty"`
	}
	AllVersions struct {
		Versions []Version `json:"versions"`
	}
)

// GetAllVersions возвращает список всех версий секрета.
func GetAllVersions(ctx context.Context, accessToken, secretId string, pageLimit, pageOffset int) (*AllVersions, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#get--v1-secrets-secretId-versions
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__show-version-secret.html#
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions")
	if pageLimit > 0 || pageOffset > 0 {
		q := make(url.Values)
		if pageLimit > 0 {
			q.Set("page.limit", strconv.Itoa(pageLimit))
		}
		if pageOffset > 0 {
			q.Set("page.offset", strconv.Itoa(pageOffset))
		}
		u.RawQuery = q.Encode()
	}
	rq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	vv, err := httphelper.ReqJson[AllVersions, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return vv, nil
}

// GetVersion возвращает метаданные версии секрета.
func GetVersion(ctx context.Context, accessToken, secretId, secretVersionId string) (*Version, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#get--v1-secrets-secretId-versions-secretVersionId
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if secretVersionId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretVersionId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions", secretVersionId)
	rq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	v, err := httphelper.ReqJson[Version, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return v, nil
}

// CreateVersion создаёт новую версию секрета.
func CreateVersion(ctx context.Context, accessToken, secretId string, payload *Payload) (*Version, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#post--v1-secrets-secretId-versions
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__add-version-secret.html#
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if payload == nil {
		return nil, errorhelper.CallerError(errors.New("no payload"))
	}
	body, err := httphelper.JsonBody(payload)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions")
	rq, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), body)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	v, err := httphelper.ReqJson[Version, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return v, nil
}

// DeleteVersion удаляет версию секрета.
func DeleteVersion(ctx context.Context, accessToken, secretId, secretVersionId string) (*Version, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#delete--v1-secrets-secretId-versions-secretVersionId
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__delete-version-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if secretVersionId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretVersionId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions", secretVersionId)
	rq, err := http.NewRequestWithContext(ctx, http.MethodDelete, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	v, err := httphelper.ReqJson[Version, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return v, nil
}

// DisableVersion отключает версию секрета.
func DisableVersion(ctx context.Context, accessToken, secretId, secretVersionId string) (*Version, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#patch--v1-secrets-secretId-versions-secretVersionId-disable
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__disable-version-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if secretVersionId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretVersionId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions", secretVersionId, "disable")
	rq, err := http.NewRequestWithContext(ctx, http.MethodPatch, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	v, err := httphelper.ReqJson[Version, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return v, nil
}

// EnableVersion включает версию секрета.
func EnableVersion(ctx context.Context, accessToken, secretId, secretVersionId string) (*Version, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#patch--v1-secrets-secretId-versions-secretVersionId-enable
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if secretVersionId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretVersionId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions", secretVersionId, "enable")
	rq, err := http.NewRequestWithContext(ctx, http.MethodPatch, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	v, err := httphelper.ReqJson[Version, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return v, nil
}

// GetPayload возвращает значение версии секрета.
func GetPayload(ctx context.Context, accessToken, secretId, secretVersionId string) (*Payload, error) {
	// https://cloud.ru/ru/docs/scsm/ug/topics/api-ref_version-secret.html#get--v1-secrets-secretId-versions-secretVersionId-payload
	// https://cloud.ru/ru/docs/scsm/ug/topics/guides__receive-version-secret.html
	if accessToken == "" {
		return nil, errorhelper.CallerError(errors.New("no accessToken"))
	}
	if secretId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretId"))
	}
	if secretVersionId == "" {
		return nil, errorhelper.CallerError(errors.New("no secretVersionId"))
	}
	u, _ := url.Parse(baseUrl)
	u.Path = path.Join(u.Path, secretId, "versions", secretVersionId, "payload")
	rq, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	rq.Header.Set("Authorization", "Bearer "+accessToken)
	p, err := httphelper.ReqJson[Payload, generichelper.NoType](http.DefaultClient, rq, httphelper.IsNotStatusOK)
	if err != nil {
		return nil, errorhelper.CallerError(err)
	}
	return p, nil
}
